package com.pvdnc.riru.system_server;

import android.annotation.SuppressLint;
import android.app.admin.DeviceAdminReceiver;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

import com.pvdnc.riru.GlobalProperties;
import com.pvdnc.riru.common.ServiceObserver;
import com.pvdnc.riru.system_server.filter.PackageFilter;
import com.pvdnc.riru.system_server.filter.PackageFilterManager;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

import andhook.lib.xposed.XC_MethodHook;
import andhook.lib.xposed.XposedBridge;

@SuppressLint("PrivateApi")
public class HookPackageManagerService extends ServiceObserver {
    private static final String TAG=HookPackageManagerService.class.getSimpleName();

    public static final HookPackageManagerService INSTANCE=new HookPackageManagerService();

    private IBinder mService;
    @Override
    public void onServiceAdd(String name, IBinder service) {
        mService=service;
        PMSUtils.systemReady(mService);

        //添加隐藏的包名
        PackageFilterManager.INSTANCE.addFilter(new PackageFilter("PVDNC") {
            @Override
            public boolean onFilter(String pkgName) {
                return pkgName.contains("com.pvdnc");
            }
        });
        //hook methods
        hookAll();
    }

    public void hookAll(){
        try {
            hookGetInstalledApplications();
            hookQueryIntentReceivers();
            hookGetBlockUninstallForUser();
        } catch (Exception e) {
            e.printStackTrace();
        }

        try{
            ClassLoader cl=mService.getClass().getClassLoader();
            Objects.requireNonNull(cl);
            //AMS不走常规的addService路线，手动hook
            new HookActivityManagerService(cl).hookAll();
            //DPM的addService有问题，手动hook
            new HookDevicePolicyService(cl).hookAll();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    private void hookGetInstalledApplications() throws Exception {
        Class<?> pmsClass=PMSUtils.get().getPMSClass();
        //public ParceledListSlice<ApplicationInfo> getInstalledApplications(int flags, int userId)
        Method method=pmsClass.getDeclaredMethod("getInstalledApplications",
                int.class,int.class);
        XposedBridge.hookMethod(method,new XC_MethodHook(){
            private int mCallerUid;
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                super.beforeHookedMethod(param);
                mCallerUid= Binder.getCallingUid();
            }

            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                Log.d(TAG,"invoke after getInstalledApplications");
                try {
                    String callingPkgName= PMSUtils.get().getPackageNameForUid(mCallerUid);
                    if(PackageFilterManager.INSTANCE.callFilter(callingPkgName)) {
                        Log.d(TAG,"hook are transparent to filtered pkg:"+callingPkgName);
                        return;
                    }
                    Object result = param.getResult();
                    Class<?> plsClass = result.getClass();
                    Log.d(TAG, "plsClass:" + plsClass);
                    //获取mList
                    Field listField = plsClass.getDeclaredField("mList");
                    listField.setAccessible(true);
                    List<ApplicationInfo> appInfoList = (List<ApplicationInfo>) listField.get(result);
                    Objects.requireNonNull(appInfoList);
                    //遍历mList
                    Iterator<ApplicationInfo> itor = appInfoList.iterator();
                    while (itor.hasNext()) {
                        ApplicationInfo info = itor.next();
                        String pkgName=info.packageName;
                        if (!PackageFilterManager.INSTANCE.callFilter(pkgName))
                            continue;
                        Log.d(TAG,"attempt to remove pkg:"+pkgName);
                        itor.remove();
                    }
                    //更新
                    listField.set(result,appInfoList);
                    Log.d(TAG,"finish patching installed app list");
                    param.setResult(result);
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        });
        Log.d(TAG,"finish hooking getInstalledApplications");
    }

    public static final boolean REMOVE_ALL_DEVICE_ADMIN=false;

    private void hookQueryIntentReceivers() throws Exception {
        Class<?> pmsClass=PMSUtils.get().getPMSClass();
        //public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags,
        //            int userId)
        Method method=pmsClass.getDeclaredMethod("queryIntentReceivers",
                Intent.class,String.class,int.class,
                int.class);
        XposedBridge.hookMethod(method,new XC_MethodHook(){
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                //Log.d(TAG,"invoke after queryIntentReceivers");//对这个方法的调用太多了
                try{
                    Object result=param.getResult();
                    Intent intent= (Intent) param.args[0];
                    Objects.requireNonNull(intent);
                    String action=intent.getAction();
                    if(!DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED
                            .equals(action)) {//目前只处理获取DeviceAdmin列表
                        return;
                    }
                    Log.d(TAG,"query device admin list");
                    if(result==null){
                        Log.w(TAG,"there is no static receivers matching intent:\n\t"+intent);
                        return;
                    }
                    List<ResolveInfo> receiverList= (List<ResolveInfo>) result;
                    //筛选receiverList
                    Iterator<ResolveInfo> itor=receiverList.iterator();
                    while (itor.hasNext()){
                        ResolveInfo info=itor.next();
                        ComponentName cn = new ComponentName(info.activityInfo.packageName,
                                info.activityInfo.name);
                        if(!REMOVE_ALL_DEVICE_ADMIN
                        &&!PackageFilterManager.INSTANCE.callFilter(cn.getPackageName()))
                            continue;
                        Log.d(TAG,"attempt to remove device admin:"+cn);
                        itor.remove();
                    }
                    //更新
                    Log.d(TAG,"finish patching receiver list");
                    param.setResult(receiverList);
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        });
        Log.d(TAG,"finish hooking queryIntentReceivers");
    }

    public void hookGetBlockUninstallForUser() throws Exception {
        Class<?> pmsClass=PMSUtils.get().getPMSClass();
        //public boolean getBlockUninstallForUser(String packageName, int userId)
        Method method=pmsClass.getDeclaredMethod("getBlockUninstallForUser",
                String.class,int.class);
        XposedBridge.hookMethod(method,new XC_MethodHook(){
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                String pkgName= (String) param.args[0];
                if(!PackageFilterManager.INSTANCE.callFilter(pkgName))
                    return;
                Log.d(TAG,"attempt to block uninstalling pkg:"+pkgName);
                param.setResult(true);
            }
        });
        Log.d(TAG,"finish hooking getBlockUninstallForUser");
    }
}
